using System;
using System.Collections.Generic;
using System.Text;
using Koda.Search;
using EnvDTE;
using EnvDTE80;
using Koda.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Koda.Utils;

namespace Koda.Search.Finders
{
    public class ImplementorFinder : IVsItemFinder
    {
        private IList<VsItem> result;
        private DTE2 dte;
        private string keyword;

        public string Keyword
        {
            get
            {
                return this.keyword;
            }
        }

        public IList<VsItem> Result
        {
            get 
            {
                return this.result;
            }
        }

        public ImplementorFinder(DTE2 dte)
        {
            if (dte == null)
                throw new ArgumentNullException("dte");

            this.dte = dte;
        }

        public int Search(string keyword)
        {
            this.keyword = keyword;
            string[] files = this.GetFiles(keyword);
            this.result = new List<VsItem>();

            foreach (string file in files)
            {
                int position = file.IndexOf(" - ");
                if (position > -1)
                {
                    string fileName = file.Substring(0, position);

                    ProjectItem projectItem = this.dte.Solution.FindProjectItem(fileName);
                    FileCodeModel fileCodeModel = projectItem.FileCodeModel;
                    CodeElement[] codeElements = VsCodeModelUtilities.GetAllCodeElementsOfKind(fileCodeModel, vsCMElement.vsCMElementClass);
                    foreach (CodeElement codeElement in codeElements)
                    {
                        CodeClass codeClass = (CodeClass)codeElement;
                        foreach (CodeElement codeInterface in codeClass.ImplementedInterfaces)
                        {
                            if (codeInterface.Name == keyword)
                            {
                                VsItem item = new VsItem(0, codeClass.Name, codeClass.FullName, fileName, VsItemType.Class);
                                this.result.Add(item);
                                break;
                            }
                        }
                    }
                }
            }

            return this.result.Count;
        }

        public bool OpenItem(VsItem item)
        {
            if (item == null)
                throw new ArgumentNullException("item");

            Window window = this.dte.ItemOperations.OpenFile(item.FileName, EnvDTE.Constants.vsViewKindPrimary);
            if (window != null)
            {
                return true;
            }
            return false;
        }

        private string[] GetFiles(string keyword)
        {
            List<string> files = new List<string>();

            IVsLibrary2 library = VsUtilities.GetIVsLibrary2(this.dte, VsUtilities.CSharpLibrary);
            IVsObjectList2 objectList = VsUtilities.SearchIVsLibrary(library, keyword, VSOBSEARCHTYPE.SO_ENTIREWORD);

            if (objectList != null)
            {
                for (uint index1 = 0; index1 < VsUtilities.IVsObjectList2Count(objectList); index1++)
                {
                    object x = null;
                    objectList.GetProperty(index1, (int)_VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_FULLNAME, out x);

                    IVsObjectList2 childObjectList = null;
                    objectList.GetList2(index1, (uint)_LIB_LISTTYPE.LLT_CLASSES, (uint)_LIB_LISTFLAGS.LLF_NONE, null, out childObjectList);

                    if (childObjectList != null)
                    {
                        for (uint index2 = 0; index2 < VsUtilities.IVsObjectList2Count(childObjectList); index2++)
                        {
                            object fullNameValue = null;
                            childObjectList.GetProperty(index2, (int)_VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_FULLNAME, out fullNameValue);
                            files.Add(fullNameValue.ToString());
                        }
                    }
                }
            }

            return files.ToArray();
        }
    }
}
